package com.zbj.homemade.spring.utils;

import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @description: ClassUtil
 * @author: Yinn
 * @create: 2019-01-11 14:02
 **/
public class ClassUtil {

    /**
     * get classLoader
     *
     * @param
     * @return ClassLoader
     * @Title: getClassLoader
     * @Description: /
     */
    public static ClassLoader getClassLoader() {

        return Thread.currentThread().getContextClassLoader();

    }

    /**
     * load class
     *
     * @param className
     * @param isInitialized  the class initialized flag for improving performance
     * @param @return
     * @return Class<?>
     * @throws
     * @Title: loadClass
     * @Description: /
     */
    public static Class<?> loadClass(String className, boolean isInitialized) {

        Class<?> cls;
        try {
            cls = Class.forName(className, isInitialized, getClassLoader());
            //Thread.currentThread().getContextClassLoader().loadClass(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        return cls;
    }

    /**
     * load class from the specific package
     *
     * @param packageName
     * @param
     * @throws
     * @Title: getClassMap
     * @Description: /
     */
    public static Map<String, Class<?>> getClassMap(String packageName) {
        Map<String, Class<?>> classMap = new HashMap<>();

        try {
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));

            while (urls.hasMoreElements()) {

                URL url = urls.nextElement();

                if (url != null) {

                    String protocol = url.getProtocol();

                    if (protocol.equals("file")) {
                        // decode
                        String packagePath = URLDecoder.decode(url.getFile(), "UTF-8");
                        // String packagePath =url.getPath().replaceAll("%20",
                        // "");
                        // add class
                        addClass(classMap, packagePath, packageName);

                    } else if (protocol.equals("jar")) {

                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();

                        if (jarURLConnection != null) {
                            JarFile jarFile = jarURLConnection.getJarFile();

                            if (jarFile != null) {

                                Enumeration<JarEntry> jarEntries = jarFile.entries();

                                while (jarEntries.hasMoreElements()) {

                                    JarEntry jarEntry = jarEntries.nextElement();

                                    String jarEntryName = jarEntry.getName();

                                    if (jarEntryName.endsWith(".class")) {

                                        String className = jarEntryName.substring(0, jarEntryName.lastIndexOf("."))
                                                .replaceAll("/", ".");
                                        doAddClass(classMap, className);

                                    }
                                }

                            }
                        }
                    }

                }

            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return classMap;
    }

    /**
     * add the file to the Map
     *
     * @param classMap
     * @param packagePath
     * @param packageName
     * @return void
     * @throws
     * @Title: addClass
     * @Description: /
     */
    private static void addClass(Map<String, Class<?>> classMap, String packagePath, String packageName) {

        File[] files = new File(packagePath).listFiles(new FileFilter() {

            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class") || file.isDirectory());
            }
        });

        for (File file : files) {

            String fileName = file.getName();

            if (file.isFile()) {
                String className = fileName.substring(0, fileName.lastIndexOf("."));

                if (StringUtils.isNotEmpty(packageName)) {

                    className = packageName + "." + className;
                }
                doAddClass(classMap, className);
            } else {
                String subPackagePath = fileName;
                if (StringUtils.isNotEmpty(packagePath)) {
                    subPackagePath = packagePath + "/" + subPackagePath;
                }

                String subPackageName = fileName;
                if (StringUtils.isNotEmpty(packageName)) {
                    subPackageName = packageName + "." + subPackageName;
                }

                addClass(classMap, subPackagePath, subPackageName);
            }
        }

    }

    private static void doAddClass(Map<String, Class<?>> classMap, String className) {

        Class<?> cls = loadClass(className, false);
        classMap.put(cls.getName(), cls);
    }

    public static String toLowerCaseFirstOne(String s){

        if (StringUtils.isBlank(s))
            return s;

        if(Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuffer()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }

    public static String toUpperCaseFirstOne(String s){

        if (StringUtils.isBlank(s))
            return s;

        if(Character.isUpperCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
    }


    public static void main(String args[]){

        Map<String, Class<?>> map = ClassUtil.getClassMap("com.zhubajie.plugin");
        for (Map.Entry<String,Class<?>> entry : map.entrySet()){
            System.out.println(entry.getKey());
        }
        System.out.println();
    }

    public static ClassLoader getDefaultClassLoader() {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        }
        catch (Throwable ex) {
            // Cannot access thread context ClassLoader - falling back...
        }
        if (cl == null) {
            // No thread context class loader -> use class loader of this class.
            cl = ClassUtil.class.getClassLoader();
            if (cl == null) {
                // getClassLoader() returning null indicates the bootstrap ClassLoader
                try {
                    cl = ClassLoader.getSystemClassLoader();
                }
                catch (Throwable ex) {
                    // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
                }
            }
        }
        return cl;
    }
}
